經過前兩天的折騰,總算把開發環境給處理好了,從孟母身上我們就可以知道環境有多麼重要,不只對人,連程式也不例外,對吧?所以讓我們從今天開始學習React
!
因為這一篇才算正式介紹起React
,所以在這裡先簡單列一下之後例子的起手式,讓大家比較不會覺得搞不懂是怎麼開始的,首先HTML
我會先建立一個id=root
的div
當作目標節點,且我們嵌入的是用webpack
打包過後的JavaScript
檔案bundle.js
:
HTML
<html>
<head>
</head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>
然後我們修改的檔案都是app.jsx
,如果有例外的話都會在另外說明,一開始會先匯入用npm
下載的react
和react-dom
:
app.jsx
import React from 'react';
import ReactDOM from 'react-dom';
webpack
也只處理app.jsx
這個檔案,本機開啟的測試port號就不一定,如果大家發現是網址是9000
就代表我是用桌電打,8080
是筆電,不過這個一點關係也沒有XD:
webpack.config.js
const path = require('path');
module.exports = {
entry: ['./app.jsx'],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './'),
},
module: {
rules: [
{ test: /.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react', '@babel/preset-env'] } } }
]
},
devServer: {
port: 9000
}
};
掰惹位,JSX
比較常用的語法可以在第一天的文章中找到,下方就不會再說明,除非有比較特別的部分,另外之後也都不會提到打包這個動作了,所以如果還不太熟可以先看前兩天關於webpack
的文章。
先來提提Element
,對有寫過HTML
的讀者來說,Element
應該不是個陌生的東西,在網頁中從一整個頁面到小則一個小按鈕都可能是一個Element
,當我們要在JSX
中建立Element
的話,最簡單的方式應該會選擇這麼做:
let title = <h1>Hello, world!</h1>
ReactDOM.render(title,document.getElementById('root'))
上方的寫法就和前幾天說的,用JSX
建立的標題是一個Element
,ReactDOM.render
中指定的document.getElementById('root')
也是一個Element
,並用ReactDOM.render
將第一個Element
放進第二個Element
中,而在一般的網頁中,把各種不同或相似的Element
給組合、拼湊在一起就會成為Components
,所以在開發React
的思維就是去思考在這個頁面中,有哪些Element
是重複出現或是相似度高的,將這些Element
用JSX
建立成一個Components
,讓每個Components
擁有重複性及可擴充性,畢竟在程式界有個名言「Don't repeat yourself」,讓我們使用React
實現它!
通常使用React
建立的Components
放到畫面上後是無法再被改變的,不過利用動畫的原理,每次都產生一個新的Components
並重新插入網頁中,就能讓畫面產生變化,值得一提的是,React
會在覆蓋前一次的Element
前自動與目前的樣子進行比對,且只會更新不同的地方,這樣就能避免掉當Element
很巨大的時候會產生的一些效能問題,如下例子:
//宣告一個匿名function
const displayTime = () =>{
//建立一個顯示目前時間的組件
let nowTime = (
<div>
/*在組件裡要註解得前後註解標記,下方則是利用大括號加入JS語法*/
<span>現在時間:{new Date().toLocaleTimeString()}</span>
</div>
)
//將上方的組件放進id為root的element中
ReactDOM.render(nowTime,document.getElementById('root'))
}
//每隔一秒重新取得時間放到畫面上
setInterval(displayTime,1000)
執行後畫面上可以看到目前的時間,且不斷的更新,但是當我們更進一步打開瀏覽器上的開發者工具,選擇Element
頁籤,就會發現和程式不同的是,現在時間:
這個固定的字串只會在第一次被放進來,之後會重複刷新的只有每次都不同的{new Date().toLocaleTimeString()}
這個部分而已:
但是從上面的例子我們也可以知道說,一個Components
被宣告後就是不可動的,那問題來了,既然他宣告後就不可動了,那如果每個組件都差了那麼一點點,不都要重新宣告?
在解決上方的問題之前,我們先為之後的寫法來學習如何用class
製作Component
,當然class
是JavaScript
的語法糖,其實比較單純的Component
也還是可以用function
來做到一樣的事情,如果不了解JavaScript
建構器的概念可以看這邊[筆記][JavaScript]使用建構器創造實體物件,只是這樣寫比較方便也比較潮。
但是為什麼上面特別註明比較單純的Component
呢?因為只有用class
建構的組件才可以有他的生命週期
在,不過這個留到下一篇文章,今天先來學基本的吧!
好的,先前我們使用直接將JSX
的物件宣告給一個變數,但現在開始要試著用class
的寫法來理解接下來的事情,以下是最簡單的例子:
//當我們宣告一個組件時,名稱第一字母必須大寫,這很重要ㄛ
class HelloTitle extends React.Component {
//render是唯一必要的屬性,會回傳一個根Element
render() {
return <h1>Hello, world!</h1>
}
}
以後不會再有的順帶一提,將上方寫法換成ES5
是這樣子的:
function HelloTitle(){
return <h1>Hello, world!</h1>
}
組件建立完成後,便可交給react-dom
來處理它:
//將剛剛建立的HelloTitle放進id為root的Element中
ReactDOM.render(<HelloTitle />,document.getElementById('root'))
是不是沒想想中那麼難?只是變成用class
去宣告而已對吧!什麼?你問說既然一樣只是宣告要怎麼做些微的調整,嘖嘖!這就要看第一個出現的小幫手Props
吧!
如果有使用過vue.js
的大大們應該對這個屬性很熟悉,Props
主要用來提供值給組件使用,不論是設定屬性或是資料,都可以透過Props
來完成,因此就算是同一個組件,也會根據提供的Props
而變得有所不同,例如:
//宣告一個Component組件
class HelloTitle extends React.Component {
render() {
{/*在花括號中使用JavaScript取得該組件的props屬性,再從中props中取name的值*/}
return <h1>Hello, {this.props.name}!</h1>
}
}
//宣告一個根Element,在裡面使用剛剛宣告的HelloTitle組件
let titleDiv = (<div>
{/*在JSX中註解需使用花括號及前後註解標記
在使用時指定name的值為GQSM,這會傳進class的props中*/}
<HelloTitle name="GQSM" />
{/*另一個組件則是指定name的值為Horse,這會傳進class的props中*/}
<HelloTitle name="Horse" />
</div>)
//將剛剛宣告的titileDiv放進root中
ReactDOM.render(titleDiv,document.getElementById('root'))
結果可以很明顯地看到,雖然都是使用HelloTtile
,但是會因為設定的props
而讓組件變得不同,而且為了保持資料流是單向的,每次傳進去總是會回傳同一個結果,props
是不可被變動的。
另外Props
是可以多層使用的:
class HelloTitle extends React.Component{
render(){
return <h1>Hello ,{this.props.name}!</h1>
}
}
//在HelloDiv組件中使用HelloTitle組件,並透過props將name值傳給HelloTitle
class HelloDiv extends React.Component{
render(){
return <div>
<HelloTitle name={this.props.name} />
</div>
}
}
//使用HelloDiv組件並將GQSM傳入name裡面
ReactDOM.render(<HelloDiv name="GQSM" />,document.getElementById('root'))
結果:
以上範例雖然只有利用props
來簡單變動組件內的內容,但其實不管是內容或是組件內的屬性也好,都可以利用這種方式去做設定,讓每個組件都可以重複利用,不會造成寫了一個A組件,卻要因為B組件的字體大小不同而又多寫一個組件的狀況發生,上述的例子可以以下方做法達成:
class HelloTitle extends React.Component {
render(){
return <p style={this.props.style}>{this.props.content}</p>
}
}
class TitleDiv extends React.Component {
render(){
return (<div>
<HelloTitle content="比較大的字" style={ {'font-size':18} } />
<HelloTitle content="比較小的字" style={ {'font-size':12} } />
</div>)
}
}
ReactDOM.render(<TitleDiv />,document.getElementById('root'))
像上方一樣在style
中傳入一個寫著樣式的物件,物件內用一個CSS屬性對一個值,並把他設給定給<p>
的style
屬性,可以完成以下的效果:
以上是關於在react
中建立Component
組件的一些例子,之後我們的使用都會圍繞在Component
上,所以最基本的使用方式一定要熟悉,下一篇文章會來提組件的狀態和生命週期,還請大家多多指教!
如果文章中有任何問題或是解釋不清楚的地方,還麻煩各位大大留言告訴我,我會盡快回答及修正文章內容,感謝大家的觀看
參考文章: